package pl.edu.agh.intobl;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import pl.edu.agh.intobl.exceptions.IntOblException;

public class RouteSet {

	List<Route> routes = new LinkedList<Route>();

	public boolean addRoute(Route route) {
		if (route.size() > 0) {
			routes.add(route);
			return true;
		}
		
		return false;
	}
	
	public int size() {
		return routes.size();
	}
	
	public int demandsNumber() {
		int demands = 0;
		for (Route route: routes)
			demands += route.size();
		
		return demands;
	}
	
	public int fitness() {
		int fitness = 0;
		for (Route route : routes)
			fitness += route.length();
		
		return fitness;
	}

	public Route selectSubRoute(Random rand) {
		Route parentRoute = routes.get(rand.nextInt(routes.size()));
		int subRouteSize = rand.nextInt(parentRoute.size()) + 1;
		int subRouteStartIndex = rand.nextInt(parentRoute.size() - subRouteSize + 1);
		Route subRoute = parentRoute.getSubRoute(subRouteStartIndex, subRouteSize);
		
		return subRoute;
	}
	
	public Route removeSubRoute(Random rand) {
		Route parentRoute = routes.get(rand.nextInt(routes.size()));
		int subRouteSize = rand.nextInt(parentRoute.size()) + 1;
		int subRouteStartIndex = rand.nextInt(parentRoute.size() - subRouteSize + 1);
		Route subRoute = parentRoute.removeSubRoute(subRouteStartIndex, subRouteSize);
		
		return subRoute;
	}

	public RouteSet diff(Route subRoute) {
		RouteSet diffRouteSet = new RouteSet();
		for (Route route : routes)
			diffRouteSet.addRoute(route.diff(subRoute));
		
		return diffRouteSet;
	}

	public void insertAfter(int demanderNr, Route subRoute) throws IntOblException {
		for (Route route : routes) {
			if (route.contains(demanderNr)) {
				route.insertAfter(demanderNr, subRoute);
				break;
			}
		}
	}
	
	@Override
	public Object clone() {
		RouteSet clonedRouteSet = new RouteSet();
		for (Route route : routes)
			clonedRouteSet.addRoute((Route) route.clone());
		
		return clonedRouteSet;
	}

	public void swapDemand(Random rand) {
		routes.get(rand.nextInt(routes.size())).swapDemand(rand);
	}

	public void inverseRandomSubRoute(Random rand) {
		routes.get(rand.nextInt(routes.size())).inverseRandomSubRoute(rand);
	}

	public void moveRandomElement(Random rand) {
		boolean moveToNewRoute = rand.nextInt(2*routes.size()) == 0 ? true : false;
		Demand demand = routes.get(rand.nextInt(routes.size())).removeRandomDemand(rand);
		
		if (moveToNewRoute) {
			Route newRoute = new Route();
			newRoute.addDemander(demand);
			addRoute(newRoute);
		} else {
			routes.get(rand.nextInt(routes.size())).insertAfterRandom(demand, rand);
		}
	}

	public void moveRandomSubRoute(Random rand) {
		boolean moveToNewRoute = rand.nextInt(2*routes.size()) == 0 ? true : false;
		Route subRoute = removeSubRoute(rand);
		
		if (moveToNewRoute) {
			addRoute(subRoute);
		} else {
			routes.get(rand.nextInt(routes.size())).insertAfterRandom(subRoute, rand);
		}
	}
	
	@Override
	public String toString() {
		return demandsNumber() + ": " + routes.toString();
	}

	public void validateRoutes() {
		RouteSet tailRouteSet = new RouteSet();
		for (Route route : routes) {
			if (route.capacity() > ProblemSolver.vehiclesCapacity) {
				tailRouteSet.routes.add(route.cutTail());
				tailRouteSet.validateRoutes();
			}
		}
		for (Route route : tailRouteSet.routes)
			routes.add(route);
	}

	public static void main(String[] args) {
		//TODO:test
		ProblemSolver.vehiclesCapacity = 100;
		
		RouteSet routeSet = new RouteSet();
		Route route = new Route();
		route.addDemander(2, 14);
		route.addDemander(3, 2);
		route.addDemander(1, 12);
		route.addDemander(4, 23);
		route.addDemander(5, 54);
		route.addDemander(7, 34);
		route.addDemander(6, 34);
		routeSet.addRoute(route);
		route = new Route();
		route.addDemander(10, 43);
		routeSet.addRoute(route);
		route = new Route();
		route.addDemander(9, 12);
		route.addDemander(8, 31);
		routeSet.addRoute(route);
		
		routeSet.validateRoutes();
	}
	
	public boolean isValid() {
		for (Route route : routes)
			if (route.capacity() > ProblemSolver.vehiclesCapacity)
				return false;
		
		return true;
	}
}
